home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue34 / timetrav / ModCal.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-03-25  |  9.1 KB  |  318 lines

  1. unit ModCal;
  2.  
  3. interface
  4.  
  5. uses Classes, Controls, Messages, Windows, Forms, Graphics, StdCtrls,
  6.   Grids, SysUtils, BaseDate;
  7.  
  8. type
  9.   TDayOfWeek = 0..6;
  10.  
  11.   TModCal = class(TCustomGrid)
  12.   private
  13.     FDate: TMJD;
  14.     FMonthOffset: Integer;
  15.     FOnChange: TNotifyEvent;
  16.     FReadOnly: Boolean;
  17.     FStartOfWeek: TDayOfWeek;
  18.     FUpdating: Boolean;
  19.     FUseCurrentDate: Boolean;
  20.     FCalSystem : TEnglishCalendar;
  21.     function GetCellText(ACol, ARow: Integer): string;
  22.     function GetDateElement(Index: Integer): Integer;
  23.     procedure SetCalendarDate(Value: TMJD);
  24.     procedure SetDateElement(Index: Integer; Value: Integer);
  25.     procedure SetStartOfWeek(Value: TDayOfWeek);
  26.     procedure SetUseCurrentDate(Value: Boolean);
  27.     function StoreCalendarDate: Boolean;
  28.   protected
  29.     procedure Change; dynamic;
  30.     procedure ChangeMonth(Delta: Integer);
  31.     procedure Click; override;
  32.     function DaysPerMonth(AYear, AMonth: Integer): Integer; virtual;
  33.     function DaysThisMonth: Integer; virtual;
  34.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
  35.     function IsLeapYear(AYear: Integer): Boolean; virtual;
  36.     function SelectCell(ACol, ARow: Longint): Boolean; override;
  37.     procedure WMSize(var Message: TWMSize); message WM_SIZE;
  38.   public
  39.     constructor Create(AOwner: TComponent); override;
  40.     property CalendarDate: TMJD  read FDate write SetCalendarDate stored StoreCalendarDate;
  41.     property CellText[ACol, ARow: Integer]: string read GetCellText;
  42.     procedure NextMonth;
  43.     procedure NextYear;
  44.     procedure PrevMonth;
  45.     procedure PrevYear;
  46.     procedure UpdateCalendar; virtual;
  47.   published
  48.     property Align;
  49.     property BorderStyle;
  50.     property Color;
  51.     property Ctl3D;
  52.     property Day: Integer index 3  read GetDateElement write SetDateElement stored False;
  53.     property Enabled;
  54.     property Font;
  55.     property GridLineWidth;
  56.     property Month: Integer index 2  read GetDateElement write SetDateElement stored False;
  57.     property ParentColor;
  58.     property ParentFont;
  59.     property ParentShowHint;
  60.     property PopupMenu;
  61.     property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
  62.     property ShowHint;
  63.     property StartOfWeek: TDayOfWeek read FStartOfWeek write SetStartOfWeek;
  64.     property TabOrder;
  65.     property TabStop;
  66.     property UseCurrentDate: Boolean read FUseCurrentDate write SetUseCurrentDate default True;
  67.     property Visible;
  68.     property Year: Integer index 1  read GetDateElement write SetDateElement stored False;
  69.     property OnClick;
  70.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  71.     property OnDblClick;
  72.     property OnDragDrop;
  73.     property OnDragOver;
  74.     property OnEndDrag;
  75.     property OnEnter;
  76.     property OnExit;
  77.     property OnKeyDown;
  78.     property OnKeyPress;
  79.     property OnKeyUp;
  80.     property OnStartDrag;
  81.   end;
  82.  
  83. implementation
  84.  
  85. constructor TModCal.Create(AOwner: TComponent);
  86. begin
  87.   inherited Create(AOwner);
  88.   { defaults }
  89.   FUseCurrentDate := True;
  90.   FixedCols := 0;
  91.   FixedRows := 1;
  92.   ColCount := 7;
  93.   RowCount := 7;
  94.   ScrollBars := ssNone;
  95.   Options := Options - [goRangeSelect] + [goDrawFocusSelected];
  96.   fCalSystem := TEnglishCalendar.create;
  97.   FDate := fCalSystem.MJDfromMSdate(Date);
  98.   UpdateCalendar;
  99. end;
  100.  
  101. procedure TModCal.Change;
  102. begin
  103.   if Assigned(FOnChange) then FOnChange(Self);
  104. end;
  105.  
  106. procedure TModCal.Click;
  107. var
  108.   TheCellText: string;
  109. begin
  110.   inherited Click;
  111.   TheCellText := CellText[Col, Row];
  112.   if TheCellText <> '' then Day := StrToInt(TheCellText);
  113. end;
  114.  
  115. function TModCal.IsLeapYear(AYear: Integer): Boolean;
  116. begin
  117.   Result := fCalSystem.IsLeapYear(aYear); //(AYear mod 4 = 0) and ((AYear mod 100 <> 0) or (AYear mod 400 = 0));
  118. end;
  119.  
  120. function TModCal.DaysPerMonth(AYear, AMonth: Integer): Integer;
  121. const
  122.   DaysInMonth: array[1..12] of Integer = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  123. begin
  124.   fCalSystem.IsLeapYear(ayear);
  125.   result := fCalSystem.MonthLength[AMonth];
  126. //  Result := DaysInMonth[AMonth];
  127. //  if (AMonth = 2) and IsLeapYear(AYear) then Inc(Result); { leap-year Feb is special }
  128. end;
  129.  
  130. function TModCal.DaysThisMonth: Integer;
  131. begin
  132.   Result := DaysPerMonth(Year, Month);
  133. end;
  134.  
  135. procedure TModCal.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  136. var
  137.   TheText: string;
  138. begin
  139.   TheText := CellText[ACol, ARow];
  140.   with ARect, Canvas do
  141.     TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
  142.       Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);
  143. end;
  144.  
  145. function TModCal.GetCellText(ACol, ARow: Integer): string;
  146. var
  147.   DayNum: Integer;
  148.   First, Last, adjustment : integer;
  149. begin
  150.   if ARow = 0
  151.     then  { day names at tops of columns }
  152.       Result := ShortDayNames[(StartOfWeek + ACol) mod 7 + 1]
  153.     else with fCalSystem do begin
  154.       adjustment := 0;
  155.       DayNum := FMonthOffset + ACol+ (ARow - 1) * 7;
  156.       if YearDef.MonthObj[Month].hasMissingDays(First, Last)
  157.         then begin
  158.           adjustment := Last - First + 1;
  159.           if DayNum > (First-1)
  160.                then DayNum := DayNum + Adjustment;
  161.           end;
  162.       if (DayNum < 1) or (DayNum > (DaysThisMonth + adjustment))
  163.         then Result := ''
  164.         else Result := IntToStr(DayNum);
  165.       end;
  166. end;
  167.  
  168. function TModCal.SelectCell(ACol, ARow: Longint): Boolean;
  169. begin
  170.   if ((not FUpdating) and FReadOnly) or (CellText[ACol, ARow] = '') then
  171.     Result := False
  172.   else Result := inherited SelectCell(ACol, ARow);
  173. end;
  174.  
  175. procedure TModCal.SetCalendarDate(Value: TMJD);
  176. begin
  177.   FDate := Value;
  178.   UpdateCalendar;
  179.   Change;
  180. end;
  181.  
  182. function TModCal.StoreCalendarDate: Boolean;
  183. begin
  184.   Result := not FUseCurrentDate;
  185. end;
  186.  
  187. function TModCal.GetDateElement(Index: Integer): Integer;
  188. var
  189.   YMD : TCalendarDate; //AYear, AMonth, ADay: Word;
  190. begin
  191.   YMD := FCalSystem.DecodeDate(FDate); //, AYear, AMonth, ADay);
  192.   case Index of
  193.     1: Result := YMD.Year;
  194.     2: Result := YMD.Month;
  195.     3: Result := YMD.Day;
  196.     else Result := -1;
  197.   end;
  198. end;
  199.  
  200. procedure TModCal.SetDateElement(Index: Integer; Value: Integer);
  201. var
  202. //  AYear, AMonth, ADay: Word;
  203.   YMD : TCalendarDate;
  204. begin
  205.   if Value > 0 then
  206.     with YMD do begin
  207.      YMD := fCalSystem.DecodeDate(FDate); //, AYear, AMonth, ADay);
  208.      case Index of
  209.        1: if Year <> Value then Year := Value else Exit;
  210.        2: if (Value <= 12) and (Value <> Month) then Month := Value else Exit;
  211.        3: if (Value <= DaysThisMonth) and (Value <> Day) then Day := Value else Exit;
  212.        else Exit;
  213.      end;
  214.      FDate := fCalSystem.EncodeDate(Year, Month, Day);
  215.      FUseCurrentDate := False;
  216.      UpdateCalendar;
  217.      Change;
  218.      end;
  219. end;
  220.  
  221. procedure TModCal.SetStartOfWeek(Value: TDayOfWeek);
  222. begin
  223.   if Value <> FStartOfWeek then
  224.   begin
  225.     FStartOfWeek := Value;
  226.     UpdateCalendar;
  227.   end;
  228. end;
  229.  
  230. procedure TModCal.SetUseCurrentDate(Value: Boolean);
  231. begin
  232.   if Value <> FUseCurrentDate then
  233.   begin
  234.     FUseCurrentDate := Value;
  235.     if Value then
  236.     begin
  237.       FDate := Date; { use the current date, then }
  238.       UpdateCalendar;
  239.     end;
  240.   end;
  241. end;
  242.  
  243. { Given a value of 1 or -1, moves to Next or Prev month accordingly }
  244. procedure TModCal.ChangeMonth(Delta: Integer);
  245. var
  246.   YMD : TCalendarDate; //AYear, AMonth, ADay: Word;
  247.   NewDate: TMJD; //DateTime;
  248.   CurDay: Integer;
  249. begin
  250.   YMD := fCalSystem.DecodeDate(FDate); //, AYear, AMonth, ADay);
  251.   with YMD do begin
  252.     CurDay := Day;
  253.     if Delta > 0
  254.       then Day := DaysPerMonth(Year, Month)
  255.       else Day := 1;
  256.     NewDate := fCalSystem.EncodeDate(Year, Month, Day);
  257.     NewDate := NewDate + Delta;
  258.     YMD := fCalSystem.DecodeDate(NewDate);
  259.     if DaysPerMonth(Year, Month) > CurDay
  260.       then Day := CurDay
  261.       else Day := DaysPerMonth(Year, Month);
  262.     CalendarDate := fCalSystem.EncodeDate(Year, Month, Day);
  263.     end;
  264. end;
  265.  
  266. procedure TModCal.PrevMonth;
  267. begin
  268.   ChangeMonth(-1);
  269. end;
  270.  
  271. procedure TModCal.NextMonth;
  272. begin
  273.   ChangeMonth(1);
  274. end;
  275.  
  276. procedure TModCal.NextYear;
  277. begin
  278.   if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
  279.   Year := Year + 1;
  280. end;
  281.  
  282. procedure TModCal.PrevYear;
  283. begin
  284.   if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
  285.   Year := Year - 1;
  286. end;
  287.  
  288. procedure TModCal.UpdateCalendar;
  289. var
  290.   YMD : TCalendarDate; //= AYear, AMonth, ADay: Word;
  291.   FirstDate: TMJD; //DateTime;
  292. begin
  293.   FUpdating := True;
  294.   try
  295.     YMD := fCalSystem.DecodeDate(FDate); //, AYear, AMonth, ADay);
  296.     FirstDate := fCalSystem.EncodeDate(Year, Month, 1);
  297.     FMonthOffset := 2 - ((fCalSystem.GetDayOfWeek(FirstDate) - StartOfWeek + 7) mod 7); { day of week for 1st of month }
  298.     if FMonthOffset = 2 then FMonthOffset := -5;
  299.     MoveColRow((Day - FMonthOffset) mod 7, (Day - FMonthOffset) div 7 + 1,
  300.       False, False);
  301.     fDate := fCalSystem.encodeDate(Year, Month, Day);
  302.     Invalidate;
  303.   finally
  304.     FUpdating := False;
  305.   end;
  306. end;
  307.  
  308. procedure TModCal.WMSize(var Message: TWMSize);
  309. var
  310.   GridLines: Integer;
  311. begin
  312.   GridLines := 6 * GridLineWidth;
  313.   DefaultColWidth := (Message.Width - GridLines) div 7;
  314.   DefaultRowHeight := (Message.Height - GridLines) div 7;
  315. end;
  316.  
  317. end.
  318.